package application.thread;

import application.ListOption;
import exception.UknownProtocolCodeException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.concurrent.Semaphore;
import protocol.ProtocolCode;
import protocol.message.AccessGranted;
import protocol.message.AccountAccess;
import protocol.message.AccountRegister;
import protocol.message.RegisterGranted;
import stream.ByteCoding;
import window.AuthenticationWindow;
import window.UserWindow;

/**
 *
 * @author Simão
 */
public class ClientThread extends ProtocolThread {

    private Semaphore clientSemaphore;

    public ClientThread(Semaphore clientSemaphore) {
        super();
        this.clientSemaphore = clientSemaphore;
    }

    @Override
    public void run() {
        ReaderThread serverReader;
        WriterThread serverWriter;
        InputStream inputStream;
        DataInputStream dataInputStream;
        OutputStream outputStream;
        DataOutputStream dataOutputStream;
        ProtocolCode protocolCode;
        AccessGranted accessGranted;
        RegisterGranted registerGranted;
        AuthenticationWindow authenticationWindow;
        UserWindow userWindow;
        Semaphore auhtentificationSemaphore;
        boolean accessOrRegisterEstabilished;
        byte[] stream;
        System.out.println("A new ClientThread has started.");
        try {
            socket = new Socket(InetAddress.getByName("192.168.1.11"), 8080);
        } catch (IOException ex) {
            System.out.println("Couldn't access Server. Aborting connection.");
            return;
        }
        System.out.println("Connection estabilished.");
        try {
            outputStream = socket.getOutputStream();
            dataOutputStream = new DataOutputStream(outputStream);
            System.out.println("Socket DataOutputStream initialized.");
            inputStream = socket.getInputStream();
            dataInputStream = new DataInputStream(inputStream);
            System.out.println("Socket DataInputStream initialized.");
        } catch (IOException ex) {
            System.out.println("IOException in socket. Aborting connection.");
            try {
                socket.close();
            } catch (IOException ex1) {
                System.out.println("IOException while closing socket.");
            }
            return;
        }
        auhtentificationSemaphore = new Semaphore(0);
        serverReader = new ReaderThread(dataInputStream, readerSemaphore, this);
        serverWriter = new WriterThread(dataOutputStream, writerSemaphore, this);
        serverReader.start();
        serverWriter.start();
        accessOrRegisterEstabilished = false;
        System.out.println("Initializing Authentification Window.");
        authenticationWindow = new AuthenticationWindow(auhtentificationSemaphore);
        authenticationWindow.setVisible(true);
        while (true) {
            try {
                auhtentificationSemaphore.acquire();
            } catch (InterruptedException ex) {
                System.out.println("InterruptedException in synchronization point. Aborting connection.");
                try {
                    socket.close();
                } catch (IOException ex1) {
                    System.out.println("IOException while closing socket.");
                }
                return;
            }
            authenticationWindow.setEnabled(false);
            if (authenticationWindow.isRegisterOperation()) {
                useWriterList(ListOption.ADD, new AccountRegister(
                        authenticationWindow.getUserName(),
                        authenticationWindow.getUserPassword()).getBytes());
            } else {
                useWriterList(ListOption.ADD, new AccountAccess(
                        authenticationWindow.getUserName(),
                        authenticationWindow.getUserPassword()).getBytes());
            }
            System.out.println("Message to server added to WriterList.");
            try {
                readerSemaphore.acquire();
            } catch (InterruptedException ex) {
                System.out.println("Connection error. Aborting connection.");
                try {
                    socket.close();
                } catch (IOException ex1) {
                    System.out.println("IOException while closing socket.");
                }
                return;
            }
            stream = useReaderList(ListOption.REMOVE, null);
            System.out.println("Message obtained from ReaderList.");
            try {
                protocolCode = ByteCoding.setAsProtocolCode(stream);
                System.out.println("Message type identified and valid.");
            } catch (UknownProtocolCodeException ex) {
                System.out.println("Message type is invalid. Ignoring message.");
                authenticationWindow.setMessageFromServer("Authentification failed! Try gain.");
                authenticationWindow.setEnabled(true);
                continue;
            }
            switch (protocolCode) {
                case ACCESS_GRANTED:
                    accessGranted = new AccessGranted(stream);
                    System.out.println(accessGranted);
                    if (accessGranted.granted()) {
                        accessOrRegisterEstabilished = true;
                    } else {
                        System.out.println("Access was not granted.");
                        authenticationWindow.setMessageFromServer("Wrong username or password.");
                        authenticationWindow.setEnabled(true);
                    }
                    break;
                case REGISTER_GRANTED:
                    registerGranted = new RegisterGranted(stream);
                    System.out.println(registerGranted);
                    if (registerGranted.granted()) {
                        accessOrRegisterEstabilished = true;
                    } else {
                        System.out.println("Register was not granted.");
                        authenticationWindow.setMessageFromServer("Username already exists.");
                        authenticationWindow.setEnabled(true);
                    }
                    break;
                default:
                    System.out.println("Message with unexcpected type received. Ignoring message.");
                    authenticationWindow.setMessageFromServer("Authentification failed! Try gain.");
                    authenticationWindow.setEnabled(true);
            }
            if (accessOrRegisterEstabilished) {
                authenticationWindow.setVisible(false);
                authenticationWindow.dispose();
                break;
            }
        }
        clientSemaphore.release();
        System.out.println("Initialazing User Window.");
        userWindow = new UserWindow(this);
        if (protocolCode == ProtocolCode.ACCESS_GRANTED) {
            /* requesting and receiving information about the user from the server */
        }
        userWindow.setVisible(true);
        while (true) {
            try {
                System.out.println("Waiting for messages from ReaderThread.");
                readerSemaphore.acquire();
            } catch (InterruptedException ex) {
                System.out.println("InterruptedException in synchronization point. Aborting connection.");
                try {
                    socket.close();
                } catch (IOException ex1) {
                    System.out.println("IOException while closing socket.");
                }
                return;
            }
            stream = useReaderList(ListOption.REMOVE, null);
            System.out.println("Message obtained from ReaderList.");
            try {
                protocolCode = ByteCoding.setAsProtocolCode(stream);
            } catch (UknownProtocolCodeException ex) {
                System.out.println("Message type is invalid. Ignoring message.");
                continue;
            }
            System.out.println("Message type identified and valid.");
            switch (protocolCode) {
                /* process other protocol message types */
                default:
                    System.out.println("Message with unexcpected type received. Ignoring message.");
            }
        }
    }
}
